<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Open Sauce 2025 Schedule</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        .visually-hidden {
            position: absolute !important;
            width: 1px;
            height: 1px;
            padding: 0;
            margin: -1px;
            overflow: hidden;
            clip: rect(0, 0, 0, 0);
            white-space: nowrap;
            border: 0;
        }

        .visually-hidden.focusable:active,
        .visually-hidden.focusable:focus {
            position: static !important;
            width: auto;
            height: auto;
            margin: 0;
            overflow: visible;
            clip: auto;
            white-space: normal;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            color: #333;
        }

        .container {
            max-width: 1200px;
            margin: 0 auto;
            padding: 20px;
        }

        .header {
            text-align: center;
            margin-bottom: 30px;
            color: white;
        }

        .header h1 {
            font-size: 2.5rem;
            margin-bottom: 10px;
            text-shadow: 2px 2px 4px rgba(0,0,0,0.3);
        }

        .header p {
            font-size: 1.2rem;
            opacity: 0.9;
        }

        .download-btn {
            background: #4CAF50;
            color: white;
            border: none;
            padding: 12px 24px;
            font-size: 1rem;
            border-radius: 8px;
            cursor: pointer;
            margin: 20px auto;
            display: block;
            box-shadow: 0 4px 8px rgba(0,0,0,0.2);
            transition: all 0.3s ease;
        }

        .download-btn:hover {
            background: #45a049;
            transform: translateY(-2px);
            box-shadow: 0 6px 12px rgba(0,0,0,0.3);
        }

        .day-tabs {
            display: flex;
            justify-content: center;
            margin-bottom: 30px;
            gap: 10px;
            flex-wrap: wrap;
        }

        .day-tab {
            background: rgba(255,255,255,0.2);
            color: white;
            border: none;
            padding: 12px 24px;
            border-radius: 25px;
            cursor: pointer;
            transition: all 0.3s ease;
            font-size: 1rem;
            backdrop-filter: blur(10px);
        }

        .day-tab.active {
            background: rgba(255,255,255,0.9);
            color: #667eea;
        }

        .day-tab:hover {
            background: rgba(255,255,255,0.3);
        }

        .day-content {
            display: none;
        }

        .day-content.active {
            display: block;
        }

        .session-card {
            background: white;
            border-radius: 12px;
            padding: 20px;
            margin-bottom: 20px;
            box-shadow: 0 4px 12px rgba(0,0,0,0.1);
            transition: transform 0.3s ease;
        }

        .session-card:hover {
            transform: translateY(-2px);
        }

        .session-header {
            display: flex;
            justify-content: space-between;
            align-items: flex-start;
            margin-bottom: 15px;
            flex-wrap: wrap;
            gap: 10px;
        }

        .session-time {
            background: #667eea;
            color: white;
            padding: 6px 12px;
            border-radius: 20px;
            font-size: 0.9rem;
            font-weight: 500;
        }

        .session-location {
            background: #f0f0f0;
            padding: 6px 12px;
            border-radius: 20px;
            font-size: 0.9rem;
            color: #666;
        }

        .session-title {
            font-size: 1.3rem;
            font-weight: 600;
            color: #333;
            margin-bottom: 10px;
        }

        .session-description {
            color: #666;
            line-height: 1.6;
            margin-bottom: 15px;
        }

        .speakers {
            display: flex;
            flex-wrap: wrap;
            gap: 12px;
            margin-top: 15px;
        }

        .speaker {
            display: flex;
            align-items: center;
            gap: 8px;
            background: #f8f9fa;
            padding: 8px 12px;
            border-radius: 20px;
            font-size: 0.9rem;
        }


        .length-badge {
            background: #ffc107;
            color: #333;
            padding: 4px 8px;
            border-radius: 12px;
            font-size: 0.8rem;
            font-weight: 500;
        }

        .loading {
            text-align: center;
            color: white;
            font-size: 1.2rem;
            margin-top: 50px;
        }
        .now-next {
            background: rgba(255,255,255,0.2);
            color: white;
            padding: 15px;
            border-radius: 12px;
            text-align: center;
            margin-bottom: 20px;
            backdrop-filter: blur(10px);
        }

        .now-next h2 {
            font-size: 1.2rem;
            margin-bottom: 5px;
        }

        .search-input {
            width: 100%;
            padding: 10px 15px;
            font-size: 1rem;
            border-radius: 8px;
            border: none;
            margin: 20px 0;
        }

        .day-heading {
            display: none;
            color: white;
            text-align: center;
            margin: 20px 0 10px;
        }

        .searching .day-tabs {
            display: none;
        }

        .searching .day-heading {
            display: block;
        }

        @media (max-width: 768px) {
            .container {
                padding: 15px;
            }
            
            .header h1 {
                font-size: 2rem;
            }
            
            .session-header {
                flex-direction: column;
                align-items: flex-start;
            }
            
            .day-tabs {
                flex-direction: column;
                align-items: center;
            }
            
            .day-tab {
                width: 200px;
                text-align: center;
            }
        }
    </style>
</head>
<body>
    <a href="#friday" class="visually-hidden focusable">Skip to main content</a>
    <div class="container">
        <div class="header">
            <h1>Open Sauce 2025</h1>
            <p>July 18-20, 2025</p>
            <button class="download-btn" onclick="downloadICS()" aria-label="Download calendar in ICS format">📅 Download Calendar (ICS)</button>
            <p style="margin-top: 10px;"><a href="https://simonwillison.net/2025/Jul/17/vibe-scraping/" style="color: white">How I built this</a></p>
        </div>

        <div id="nowNext" class="now-next"></div>
        <input id="searchInput" class="search-input" type="search" placeholder="Search sessions..." aria-label="Search sessions">

        <div class="day-tabs" role="tablist">
            <button id="tab-friday" class="day-tab active" role="tab" aria-controls="friday" aria-selected="true" onclick="showDay('friday', event)">Friday 18th</button>
            <button id="tab-saturday" class="day-tab" role="tab" aria-controls="saturday" aria-selected="false" tabindex="-1" onclick="showDay('saturday', event)">Saturday 19th</button>
            <button id="tab-sunday" class="day-tab" role="tab" aria-controls="sunday" aria-selected="false" tabindex="-1" onclick="showDay('sunday', event)">Sunday 20th</button>
        </div>

        <div class="loading" id="loading" role="status" aria-live="polite">Loading schedule...</div>

        <h2 class="day-heading">Friday 18th</h2>
        <div id="friday" class="day-content active" role="tabpanel" aria-labelledby="tab-friday"></div>

        <h2 class="day-heading">Saturday 19th</h2>
        <div id="saturday" class="day-content" role="tabpanel" aria-labelledby="tab-saturday" aria-hidden="true"></div>

        <h2 class="day-heading">Sunday 20th</h2>
        <div id="sunday" class="day-content" role="tabpanel" aria-labelledby="tab-sunday" aria-hidden="true"></div>
    </div>

    <script>
        let scheduleData = {};
        let allSessions = [];

        async function loadSchedule() {
            try {
                const response = await fetch('https://raw.githubusercontent.com/simonw/.github/efd800df75d2804e502b2f8563503f669747dec5/schedule.json');
                if (!response.ok) {
                    throw new Error(`HTTP error! status: ${response.status}`);
                }
                scheduleData = await response.json();
                buildAllSessions();
                updateNowAndNext();
                setInterval(updateNowAndNext, 60000);
                
                renderSchedule();
                document.getElementById('loading').style.display = 'none';
            } catch (error) {
                console.error('Error loading schedule:', error);
                document.getElementById('loading').textContent = 'Error loading schedule';
            }
        }

        function renderSchedule(filter = "") {
            const days = ['friday', 'saturday', 'sunday'];
            const f = filter.toLowerCase();

            days.forEach(day => {
                const dayContainer = document.getElementById(day);
                let sessions = scheduleData[day] || [];
                if (f) {
                    sessions = sessions.filter(session => {
                        const combined = [
                            session.time,
                            session.title,
                            session.description,
                            session.where,
                            session.speakers ? session.speakers.map(s => s.name).join(' ') : '',
                            session.moderator ? session.moderator.name : ''
                        ].join(' ').toLowerCase();
                        return combined.includes(f);
                    });
                }
                if (sessions.length === 0) {
                    dayContainer.innerHTML = '<p style="color:white">No sessions found.</p>';
                    return;
                }
                dayContainer.innerHTML = sessions.map(session => `
                    <div class="session-card">
                        <div class="session-header">
                            <div>
                                <span class="session-time">${session.time}</span>
                                <span class="length-badge">${session.length} min</span>
                            </div>
                            <div class="session-location">${session.where}</div>
                        </div>
                        <h3 class="session-title">${session.title}</h3>
                        <div class="session-description">${session.description}</div>
                        ${session.speakers && session.speakers.length > 0 ? `
                            <div class="speakers">
                                ${session.speakers.map(speaker => `
                                    <div class="speaker">
                                        <span>${speaker.name}</span>
                                    </div>
                                `).join('')}
                            </div>
                        ` : ''}
                        ${session.moderator ? `
                            <div class="speakers">
                                <div class="speaker" style="background: #e3f2fd;">
                                    <span><strong>Moderator:</strong> ${session.moderator.name}</span>
                                </div>
                            </div>
                        ` : ''}
                    </div>
                `).join('');
            });
        }

        function showDay(day, e) {
            // Hide all day contents
            document.querySelectorAll('.day-content').forEach(content => {
                content.classList.remove('active');
                content.setAttribute('aria-hidden', 'true');
            });

            // Update all tabs
            document.querySelectorAll('.day-tab').forEach(tab => {
                tab.classList.remove('active');
                tab.setAttribute('aria-selected', 'false');
                tab.setAttribute('tabindex', '-1');
            });

            // Show selected day content
            const panel = document.getElementById(day);
            panel.classList.add('active');
            panel.setAttribute('aria-hidden', 'false');

            // Add active class to clicked tab
            const tab = e.currentTarget;
            tab.classList.add('active');
            tab.setAttribute('aria-selected', 'true');
            tab.removeAttribute('tabindex');
            tab.focus();
        }

        function timeToMinutes(timeStr) {
            const [time, period] = timeStr.split(' ');
            const [hours, minutes] = time.split(':').map(Number);
            let totalMinutes = hours * 60 + minutes;
            
            if (period === 'PM' && hours !== 12) {
                totalMinutes += 12 * 60;
            } else if (period === 'AM' && hours === 12) {
                totalMinutes = minutes;
            }
            
            return totalMinutes;
        }

        function minutesToTime(minutes) {
            const hours = Math.floor(minutes / 60);
            const mins = minutes % 60;
            return `${hours.toString().padStart(2, '0')}${mins.toString().padStart(2, '0')}00`;
        }

        function buildAllSessions() {
            const order = ['friday', 'saturday', 'sunday'];
            allSessions = [];
            order.forEach((day, i) => {
                const sessions = scheduleData[day] || [];
                sessions.forEach(session => {
                    const start = timeToMinutes(session.time) + i * 1440;
                    const end = start + parseInt(session.length);
                    allSessions.push({ ...session, day, start, end });
                });
            });
            allSessions.sort((a, b) => a.start - b.start);
        }

        function updateNowAndNext() {
            if (!allSessions.length) return;
            const startDate = new Date('2025-07-18T00:00:00-07:00');
            const pacificNow = new Date(new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }));
            const minutesNow = Math.floor((pacificNow - startDate) / 60000);

            let current = null;
            let next = null;
            for (const session of allSessions) {
                if (minutesNow >= session.start && minutesNow < session.end) {
                    current = session;
                } else if (session.start > minutesNow && !next) {
                    next = session;
                }
            }

            const container = document.getElementById('nowNext');
            let html = '';
            if (current) {
                html += `<h2>Happening Now</h2><p>${current.time} – ${current.title} (${current.where})</p>`;
            }
            if (next) {
                html += `<h2 style="margin-top:10px;">Up Next</h2><p>${next.time} – ${next.title} (${next.where})</p>`;
            }
            container.innerHTML = html || '<p>No upcoming sessions.</p>';
        }

        function downloadICS() {
            if (!scheduleData || Object.keys(scheduleData).length === 0) {
                alert('Schedule not loaded yet. Please wait a moment and try again.');
                return;
            }

            const icsContent = generateICS();
            const blob = new Blob([icsContent], { type: 'text/calendar' });
            const url = URL.createObjectURL(blob);
            const a = document.createElement('a');
            a.href = url;
            a.download = 'open-sauce-2025-schedule.ics';
            document.body.appendChild(a);
            a.click();
            document.body.removeChild(a);
            URL.revokeObjectURL(url);
        }

        function generateICS() {
            const dayDates = {
                friday: '20250718',
                saturday: '20250719',
                sunday: '20250720'
            };

            let icsContent = `BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//Open Sauce//Open Sauce 2025//EN
CALSCALE:GREGORIAN
METHOD:PUBLISH
X-WR-CALNAME:Open Sauce 2025
X-WR-CALDESC:Open Sauce 2025 Event Schedule
X-WR-TIMEZONE:America/Los_Angeles
BEGIN:VTIMEZONE
TZID:America/Los_Angeles
BEGIN:STANDARD
DTSTART:20241103T020000
RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
TZOFFSETFROM:-0700
TZOFFSETTO:-0800
TZNAME:PST
END:STANDARD
BEGIN:DAYLIGHT
DTSTART:20250309T020000
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
TZOFFSETFROM:-0800
TZOFFSETTO:-0700
TZNAME:PDT
END:DAYLIGHT
END:VTIMEZONE
`;

            Object.keys(scheduleData).forEach(day => {
                const sessions = scheduleData[day];
                const dateStr = dayDates[day];

                sessions.forEach((session, index) => {
                    const startMinutes = timeToMinutes(session.time);
                    const endMinutes = startMinutes + parseInt(session.length);
                    
                    const startTime = minutesToTime(startMinutes);
                    const endTime = minutesToTime(endMinutes);
                    
                    const speakers = session.speakers ? session.speakers.map(s => s.name).join(', ') : '';
                    const moderator = session.moderator ? `Moderator: ${session.moderator.name}` : '';
                    const speakerInfo = [speakers, moderator].filter(s => s).join('\n');
                    
                    const description = [
                        session.description,
                        speakerInfo ? `\n${speakerInfo}` : ''
                    ].filter(d => d).join('');

                    icsContent += `BEGIN:VEVENT
DTSTART;TZID=America/Los_Angeles:${dateStr}T${startTime}
DTEND;TZID=America/Los_Angeles:${dateStr}T${endTime}
SUMMARY:${session.title}
DESCRIPTION:${description.replace(/\n/g, '\\n')}
LOCATION:${session.where}
UID:opensauce2025-${day}-${index}@opensauce.com
DTSTAMP:20250101T000000Z
END:VEVENT
`;
                });
            });

            icsContent += 'END:VCALENDAR';
            return icsContent;
        }

        document.getElementById('searchInput').addEventListener('input', e => {
            const filter = e.target.value;
            renderSchedule(filter);

            const container = document.querySelector('.container');
            if (filter.trim()) {
                container.classList.add('searching');
                document.querySelectorAll('.day-content').forEach(content => {
                    content.classList.add('active');
                    content.setAttribute('aria-hidden', 'false');
                });
            } else {
                container.classList.remove('searching');
                const activeTab = document.querySelector('.day-tab.active') || document.getElementById('tab-friday');
                const day = activeTab.id.replace('tab-', '');
                showDay(day, { currentTarget: activeTab });
            }
        });

        // Load schedule when page loads
        loadSchedule();
    </script>
</body>
</html>
